#include "IServlet.h"
#include <iostream>
#include <string>
#include <thread>
#include <list>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <string.h>
#include <IXML.h>
#include "IPlugin.h"
template<typename T>
class MessageProcess
{
    public:
        struct DATA {
            T data;
            std::function<void(T&data)> handle;
        };
        void runIn(T i,std::function<void(T&data)> handle)
        {
            std::unique_lock<std::mutex> uniqlock(my_mutex);
            DATA  data;
            data.data = i;
            data.handle = handle;

            msglist.push_back(data);
            my_cond.notify_one();
        }


        void Outmsglist()
        {
            DATA command;
            while (true)
            {
                std::unique_lock < std::mutex > uniqlock(my_mutex);
                my_cond.wait(uniqlock, [this] {
                   if (in_thread_exit)
                   {
                       printf("stopped\n");
                       return true;
                   }  
                   if (!msglist.empty())
                        return true;
                   return false;
                });

                if(in_thread_exit && msglist.empty()){
                 
                    break;
                }
                if (msglist.empty()) {continue;}
                command = msglist.front();
                msglist.pop_front();
             
                command.handle(command.data);
                uniqlock.unlock();

            }
        }
        void exit()
        {
            in_thread_exit = true;
            std::unique_lock < std::mutex > uniqlock(my_mutex);
            msglist.clear();
            my_cond.notify_one();
        }
    private:
        std::list<DATA> msglist;
        std::mutex my_mutex;
        std::condition_variable my_cond;
        bool in_thread_exit = false;
};

 

class ThreadSession : public ISession {
public:
    IServlet *self;
    IServlet *target;
    ThreadSession(IServlet *self,IServlet *target) {
        this->self = self;
        this->target = target;
    }
    virtual void reply(const void *data,int len) ;
    virtual void close(){

    }
};
class ThreadServlet : public IServlet {
public:
    std::string name;
    std::string target;
    virtual void onLoad(INode *node) {
       
       this->name = node->getStr("name");
       INode * connection = node->childNode("connection");
       if (connection) {
            
            this->target = connection->getStr("target");
       }
       
        start();
    }
    virtual void onUnload(){
        wait();
    }
    virtual void on(SERVLET_STATUS type,std::function<int(ISession *,void*,int)> handler) {
        msgidHandler = handler;
    }
    virtual void onMessage(ISession *session,void *data,int size) {
        msgidHandler(session,data,size);
    }
    virtual void onStatus(SERVLET_STATUS status) {

    }
    virtual void request(const void *data,int len)  {
        ThreadServlet * targetServlet = (ThreadServlet*)Bean<IServlet>(this->target.c_str()).pointer();
        if (targetServlet) {
            std::string msg;
            msg.resize(len);
            memcpy(&msg[0],data,len);

            ThreadSession session(this,targetServlet);
            printf("request target:%s :%p self:%p\n",target.c_str(),targetServlet,this);
            targetServlet->pushMsgToQueue(msg, [=](std::string&data){
                targetServlet->onMessage((ThreadSession*)&session,&data[0],data.size());
            });
        }
       
    }
    void pushMsgToQueue(const std::string &msg,std::function<void(std::string&data)> handle) {
        mpobj.runIn(msg, handle);
    }
    
  
    std::function<int(ISession *,void*,int)>  msgidHandler;
    virtual void start() 
    {
        thread  = new std::thread(&MessageProcess<std::string>::Outmsglist, &mpobj);
    }
    virtual void wait() {
        if (thread) {
            printf("ThreadServlet::wait\n");
            thread->join();
        }
    }
    virtual void stop(){
        mpobj.exit();
    }
    std::thread *thread;
    MessageProcess<std::string> mpobj;
    ~ThreadServlet(){
        if (thread) delete thread;
        thread = 0;
    }
};

void ThreadSession::reply(const void *data,int len)  {
    std::string msg;
    msg.resize(len);
    memcpy(&msg[0],data,len);
    ThreadSession session(target,self);
    ThreadServlet *servlet = (ThreadServlet*) self;
    servlet->pushMsgToQueue(msg, [=](std::string&data){
        servlet->onMessage((ThreadSession*)&session,&data[0],data.size());
    });
}

 

REG_PLUGIN(ThreadServlet);